﻿namespace Stopwatch.Core.Persistence
{
    using Newtonsoft.Json;
    using Stopwatch.Exceptions;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Threading;

    public class StopWatch : IWatch
    {
        const int PERIOD = 10;

        public DateTime Beginning { get; set; }

        public DateTime SplitBeginning { get; set; }

        public DateTime Pausing { get; set; }

        Timer timer;

        public StopWatch()
        {
            Splits = new Collection<Time>();
            Runtime = Time.Zero;
            SubRuntime = Time.Zero;
            timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);

            Status = Core.Status.Ready;
        }

        private void TimerCallback(object state)
        {
            var now = DateTime.Now;

            Runtime = (now - Beginning).ToTime();
            SubRuntime = (now - SplitBeginning).ToTime();

            if (OnTick != null)
            {
                OnTick(this, EventArgs.Empty);
            }
        }
        
        #region IWatch properties

        public Time Runtime { get; set; }

        public Time SubRuntime { get; set; }

        public ICollection<Time> Splits { get; set; }

        public Status Status { get; set; }

        #endregion

        #region IWatch methods

        public void Start()
        {
            switch (Status)
            {
                case Status.Ready:
                    Beginning = DateTime.Now;
                    SplitBeginning = Beginning;
                    Pausing = Beginning;
                    break;

                case Status.Paused:
                    Beginning += (DateTime.Now - Pausing);
                    SplitBeginning += (DateTime.Now - Pausing);
                    break;
            }

            timer.Change(PERIOD, PERIOD);
            Status = Status.Started;
        }

        public void Pause()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            Status = Status.Paused;
            Pausing = DateTime.Now;
            if (OnPaused != null)
            {
                OnPaused(this, EventArgs.Empty);
            }
        }

        public Time Split()
        {
            SplitBeginning = DateTime.Now;
            Time split = SubRuntime;
            SubRuntime = Time.Zero;

            Splits.Add(split);

            return split;
        }

        public void Reset()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);

            Status = Status.Stoped;

            Splits.Clear();
            Runtime = Time.Zero;
            SubRuntime = Time.Zero;

            if (OnStoped != null)
            {
                OnStoped(this, EventArgs.Empty);
            }

            Status = Status.Ready;
        }

        #endregion

        #region IWatch events

        public event EventHandler OnPaused;

        public event EventHandler OnStoped;

        public event EventHandler OnTick;

        #endregion

        public void Dispose()
        {
            if (timer != null)
            {
                timer.Dispose();
                timer = null;
            }
        }
    }
}
